๐๏ธGitะฏัะฐ๐๏ธ
Node / meshtastic / Meshtastic-Android / files / core / testing / src / commonMain / kotlin / org / meshtastic / core / testing / FakeBle.kt
Displaying Raw โข Download
core/testing/src/commonMain/kotlin/org/meshtastic/core/testing/FakeBle.kt 41414f832f938b655c97bc07509bc140ea16e2a3 (41414f83) Text, 18.08 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.testing
Tff7b72import T7ee787kotlinx.coroutines.CoroutineScope
Tff7b72import T7ee787kotlinx.coroutines.Dispatchers
Tff7b72import T7ee787kotlinx.coroutines.flow.Flow
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableSharedFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.StateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.asStateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.emitAll
Tff7b72import T7ee787kotlinx.coroutines.flow.flow
Tff7b72import T7ee787kotlinx.coroutines.flow.onStart
Tff7b72import T7ee787org.meshtastic.core.ble.BleCharacteristic
Tff7b72import T7ee787org.meshtastic.core.ble.BleConnection
Tff7b72import T7ee787org.meshtastic.core.ble.BleConnectionFactory
Tff7b72import T7ee787org.meshtastic.core.ble.BleConnectionState
Tff7b72import T7ee787org.meshtastic.core.ble.BleDevice
Tff7b72import T7ee787org.meshtastic.core.ble.BleScanner
Tff7b72import T7ee787org.meshtastic.core.ble.BleService
Tff7b72import T7ee787org.meshtastic.core.ble.BleWriteType
Tff7b72import T7ee787org.meshtastic.core.ble.BluetoothRepository
Tff7b72import T7ee787org.meshtastic.core.ble.BluetoothState
Tff7b72import T7ee787org.meshtastic.core.ble.DisconnectReason
Tff7b72import T7ee787kotlin.time.Duration
Tff7b72import T7ee787kotlin.uuid.Uuid
Tff7b72class T56d364FakeBleDeviceTb4b4b4(
Tff7b72override Tff7b72val Te6edf3addressTb4b4b4: Tffa657StringTb4b4b4,
Tff7b72override Tff7b72val Te6edf3nameTb4b4b4: Tffa657String? Tff7b72= Ta5d6ff"Ta5d6ffFake DeviceTa5d6ff"Tb4b4b4,
Te6edf3initialStateTb4b4b4: Te6edf3BleConnectionState Tff7b72= Te6edf3BleConnectionStateTb4b4b4.Te6edf3DisconnectedTb4b4b4(Tb4b4b4)Tb4b4b4,
Tff7b72override Tff7b72val Te6edf3rssiTb4b4b4: Tffa657Int? Tff7b72= Tff7b72nullTb4b4b4,
Tb4b4b4) Tb4b4b4: Te6edf3BaseFakeTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3BleDevice Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3_state Tff7b72= Te6edf3mutableStateFlowTb4b4b4(Te6edf3initialStateTb4b4b4)
Tff7b72override Tff7b72val Te6edf3stateTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3BleConnectionStateTff7b72> Tff7b72= Te6edf3_stateTb4b4b4.Te6edf3asStateFlowTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3_isBonded Tff7b72= Te6edf3mutableStateFlowTb4b4b4(Tff7b72falseTb4b4b4)
Tff7b72override Tff7b72val Te6edf3isBondedTb4b4b4: Tffa657Boolean
Tff7b72getTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3_isBondedTb4b4b4.Te6edf3value
Tff7b72override Tff7b72val Te6edf3isConnectedTb4b4b4: Tffa657Boolean
Tff7b72getTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3_stateTb4b4b4.Te6edf3value Tff7b72=Tff7b72= Te6edf3BleConnectionStateTb4b4b4.Te6edf3Connected
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffreadRssiTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657Int? Tff7b72= Te6edf3rssi
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffbondTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3_isBondedTb4b4b4.Te6edf3value Tff7b72= Tff7b72true
Tb4b4b4}
Tff7b72fun Td2a8ffsetStateTb4b4b4(Te6edf3newStateTb4b4b4: Te6edf3BleConnectionStateTb4b4b4) Tb4b4b4{
Te6edf3_stateTb4b4b4.Te6edf3value Tff7b72= Te6edf3newState
Tb4b4b4}
Tb4b4b4}
Tff7b72class T56d364FakeBleScanner Tb4b4b4:
Te6edf3BaseFakeTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3BleScanner Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3foundDevices Tff7b72= Te6edf3mutableSharedFlowTff7b72<Te6edf3BleDeviceTff7b72>Tb4b4b4(Te6edf3replay Tff7b72= T79c0ff1T79c0ff0Tb4b4b4)
Tff7b72var Te6edf3lastScanServiceUuidTb4b4b4: Te6edf3Uuid? Tff7b72= Tff7b72null
Te6edf3private Tff7b72set
Tff7b72var Te6edf3lastScanAddressTb4b4b4: Tffa657String? Tff7b72= Tff7b72null
Te6edf3private Tff7b72set
Tff7b72override Tff7b72fun Td2a8ffscanTb4b4b4(Te6edf3timeoutTb4b4b4: Te6edf3DurationTb4b4b4, Te6edf3serviceUuidTb4b4b4: Te6edf3Uuid?Tb4b4b4, Te6edf3addressTb4b4b4: Tffa657String?Tb4b4b4)Tb4b4b4: Te6edf3FlowTff7b72<Te6edf3BleDeviceTff7b72> Tb4b4b4{
Te6edf3lastScanServiceUuid Tff7b72= Te6edf3serviceUuid
Te6edf3lastScanAddress Tff7b72= Te6edf3address
Tff7b72return Te6edf3flow Tb4b4b4{ Te6edf3emitAllTb4b4b4(Te6edf3foundDevicesTb4b4b4) Tb4b4b4}
Tb4b4b4}
Tff7b72fun Td2a8ffemitDeviceTb4b4b4(Te6edf3deviceTb4b4b4: Te6edf3BleDeviceTb4b4b4) Tb4b4b4{
Te6edf3foundDevicesTb4b4b4.Te6edf3tryEmitTb4b4b4(Te6edf3deviceTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72class T56d364FakeBleConnection Tb4b4b4:
Te6edf3BaseFakeTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3BleConnection Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3_device Tff7b72= Te6edf3mutableStateFlowTff7b72<Te6edf3BleDevice?Tff7b72>Tb4b4b4(Tff7b72nullTb4b4b4)
Tff7b72override Tff7b72val Te6edf3deviceTb4b4b4: Te6edf3BleDevice?
Tff7b72getTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3_deviceTb4b4b4.Te6edf3value
Tff7b72override Tff7b72val Te6edf3deviceFlowTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3BleDevice?Tff7b72> Tff7b72= Te6edf3_deviceTb4b4b4.Te6edf3asStateFlowTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3_connectionState Tff7b72= Te6edf3mutableStateFlowTff7b72<Te6edf3BleConnectionStateTff7b72>Tb4b4b4(Te6edf3BleConnectionStateTb4b4b4.Te6edf3DisconnectedTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72override Tff7b72val Te6edf3connectionStateTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3BleConnectionStateTff7b72> Tff7b72= Te6edf3_connectionStateTb4b4b4.Te6edf3asStateFlowTb4b4b4(Tb4b4b4)
T8b949e/** When > 0, the next [failNextN] calls to [connectAndAwait] return [BleConnectionState.Disconnected]. */
Tff7b72var Te6edf3failNextNTb4b4b4: Tffa657Int Tff7b72= T79c0ff0
T8b949e/** When non-null, [connectAndAwait] throws this exception instead of connecting. */
Tff7b72var Te6edf3connectExceptionTb4b4b4: Te6edf3Exception? Tff7b72= Tff7b72null
T8b949e/** Negotiated write length exposed to callers; `null` means unknown / not negotiated. */
Tff7b72var Te6edf3maxWriteValueLengthTb4b4b4: Tffa657Int? Tff7b72= Tff7b72null
T8b949e/** Result returned by [invalidateServiceCache]; defaults to `false` to match the [BleConnection] default. */
Tff7b72var Te6edf3invalidateServiceCacheResultTb4b4b4: Tffa657Boolean Tff7b72= Tff7b72false
T8b949e/** Number of times [invalidateServiceCache] has been invoked. */
Tff7b72var Te6edf3invalidateServiceCacheCallsTb4b4b4: Tffa657Int Tff7b72= T79c0ff0
T8b949e/** Optional callback invoked at the end of [disconnect] (e.g. seed freshly-discovered services on reconnect). */
Tff7b72var Te6edf3onDisconnectTb4b4b4: Tb4b4b4(Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4)Tff7b72? Tff7b72= Tff7b72null
T8b949e/** Number of times [disconnect] has been invoked. */
Tff7b72var Te6edf3disconnectCallsTb4b4b4: Tffa657Int Tff7b72= T79c0ff0
T8b949e/** Number of times [connectAndAwait] has been invoked (including failures). */
Tff7b72var Te6edf3connectAndAwaitCallsTb4b4b4: Tffa657Int Tff7b72= T79c0ff0
T8b949e/** Number of times [profile] has been invoked. */
Tff7b72var Te6edf3profileCallsTb4b4b4: Tffa657Int Tff7b72= T79c0ff0
T8b949e/** Externally simulate a remote disconnect (e.g. node power-cycle) for tests that exercise reconnect. */
Tff7b72fun Td2a8ffsimulateRemoteDisconnectTb4b4b4(Te6edf3reasonTb4b4b4: Te6edf3DisconnectReason Tff7b72= Te6edf3DisconnectReasonTb4b4b4.Te6edf3TimeoutTb4b4b4) Tb4b4b4{
Te6edf3_connectionStateTb4b4b4.Te6edf3value Tff7b72= Te6edf3BleConnectionStateTb4b4b4.Te6edf3DisconnectedTb4b4b4(Te6edf3reasonTb4b4b4)
Tb4b4b4}
T8b949e/** Service UUIDs that should appear missing โ `profile()` throws `NoSuchElementException` for these. */
Tff7b72val Te6edf3missingServicesTb4b4b4: Te6edf3MutableSetTff7b72<Te6edf3UuidTff7b72> Tff7b72= Te6edf3mutableSetOfTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3service Tff7b72= Te6edf3FakeBleServiceTb4b4b4(Tb4b4b4)
T8b949e/**
* Selects the service exposed to each [profile] call. The argument is the 1-based [profile] call ordinal (the first
* call receives `1`). Defaults to the shared [service].
*
* A [profile] call that fails because [missingServices] is true still consumes an ordinal. The ordinal counts
* profile invocations, not provider invocations.
*/
Tff7b72var Te6edf3profileServiceProviderTb4b4b4: Tb4b4b4(Te6edf3profileCallTb4b4b4: Tffa657IntTb4b4b4) Tff7b72-Tff7b72> Te6edf3BleService Tff7b72= Tb4b4b4{ Te6edf3service Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffconnectTb4b4b4(Te6edf3deviceTb4b4b4: Te6edf3BleDeviceTb4b4b4) Tb4b4b4{
Te6edf3_deviceTb4b4b4.Te6edf3value Tff7b72= Te6edf3device
Te6edf3_connectionStateTb4b4b4.Te6edf3value Tff7b72= Te6edf3BleConnectionStateTb4b4b4.Te6edf3Connecting
Tff7b72if Tb4b4b4(Te6edf3device Tff7b72is Te6edf3FakeBleDeviceTb4b4b4) Tb4b4b4{
Te6edf3deviceTb4b4b4.Te6edf3setStateTb4b4b4(Te6edf3BleConnectionStateTb4b4b4.Te6edf3ConnectingTb4b4b4)
Tb4b4b4}
Te6edf3_connectionStateTb4b4b4.Te6edf3value Tff7b72= Te6edf3BleConnectionStateTb4b4b4.Te6edf3Connected
Tff7b72if Tb4b4b4(Te6edf3device Tff7b72is Te6edf3FakeBleDeviceTb4b4b4) Tb4b4b4{
Te6edf3deviceTb4b4b4.Te6edf3setStateTb4b4b4(Te6edf3BleConnectionStateTb4b4b4.Te6edf3ConnectedTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffconnectAndAwaitTb4b4b4(Te6edf3deviceTb4b4b4: Te6edf3BleDeviceTb4b4b4, Te6edf3timeoutTb4b4b4: Te6edf3DurationTb4b4b4)Tb4b4b4: Te6edf3BleConnectionState Tb4b4b4{
Te6edf3connectAndAwaitCallsTff7b72+Tff7b72+
Te6edf3connectExceptionTff7b72?.Te6edf3let Tb4b4b4{ Tff7b72throw Tffa657it Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3failNextN Tff7b72> T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3failNextNTff7b72-Tff7b72-
Tff7b72return Te6edf3BleConnectionStateTb4b4b4.Te6edf3DisconnectedTb4b4b4(Tb4b4b4)
Tb4b4b4}
Te6edf3connectTb4b4b4(Te6edf3deviceTb4b4b4)
Tff7b72return Te6edf3BleConnectionStateTb4b4b4.Te6edf3Connected
Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffdisconnectTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3disconnectCallsTff7b72+Tff7b72+
Tff7b72val Te6edf3currentDevice Tff7b72= Te6edf3_deviceTb4b4b4.Te6edf3value
Te6edf3_connectionStateTb4b4b4.Te6edf3value Tff7b72= Te6edf3BleConnectionStateTb4b4b4.Te6edf3DisconnectedTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3currentDevice Tff7b72is Te6edf3FakeBleDeviceTb4b4b4) Tb4b4b4{
Te6edf3currentDeviceTb4b4b4.Te6edf3setStateTb4b4b4(Te6edf3BleConnectionStateTb4b4b4.Te6edf3DisconnectedTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
Te6edf3_deviceTb4b4b4.Te6edf3value Tff7b72= Tff7b72null
Te6edf3onDisconnectTff7b72?.Te6edf3invokeTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Tff7b72<Te6edf3TTff7b72> Td2a8ffprofileTb4b4b4(
Te6edf3serviceUuidTb4b4b4: Te6edf3UuidTb4b4b4,
Te6edf3timeoutTb4b4b4: Te6edf3DurationTb4b4b4,
Te6edf3setupTb4b4b4: Te6edf3suspend Te6edf3CoroutineScopeTb4b4b4.Tb4b4b4(Te6edf3BleServiceTb4b4b4) Tff7b72-Tff7b72> Te6edf3TTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3T Tb4b4b4{
Te6edf3profileCallsTff7b72+Tff7b72+
Tff7b72if Tb4b4b4(Te6edf3serviceUuid Tff7b72in Te6edf3missingServicesTb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3NoSuchElementExceptionTb4b4b4(Ta5d6ff"Ta5d6ffService Tffd700$Te6edf3serviceUuidTa5d6ff not foundTa5d6ff"Tb4b4b4)
Tb4b4b4}
T8b949e// Use Dispatchers.Unconfined so notification emissions are delivered synchronously to
T8b949e// collectors (write โ immediate notification). This matches the original FakeBleConnection
T8b949e// contract and the auto-responding pattern used by DFU/OTA transport tests.
Tff7b72return Te6edf3CoroutineScopeTb4b4b4(Te6edf3DispatchersTb4b4b4.Te6edf3UnconfinedTb4b4b4)Tb4b4b4.Te6edf3setupTb4b4b4(Te6edf3profileServiceProviderTb4b4b4(Te6edf3profileCallsTb4b4b4)Tb4b4b4)
Tb4b4b4}
Tff7b72override Tff7b72fun Td2a8ffmaximumWriteValueLengthTb4b4b4(Te6edf3writeTypeTb4b4b4: Te6edf3BleWriteTypeTb4b4b4)Tb4b4b4: Tffa657Int? Tff7b72= Te6edf3maxWriteValueLength
Tff7b72override Tff7b72fun Td2a8ffinvalidateServiceCacheTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Te6edf3invalidateServiceCacheCallsTff7b72+Tff7b72+
Tff7b72return Te6edf3invalidateServiceCacheResult
Tb4b4b4}
Tb4b4b4}
Tff7b72class T56d364FakeBleWriteTb4b4b4(Tff7b72val Te6edf3characteristicTb4b4b4: Te6edf3BleCharacteristicTb4b4b4, Tff7b72val Te6edf3dataTb4b4b4: Te6edf3ByteArrayTb4b4b4, Tff7b72val Te6edf3writeTypeTb4b4b4: Te6edf3BleWriteTypeTb4b4b4) Tb4b4b4{
Tff7b72override Tff7b72fun Td2a8ffequalsTb4b4b4(Te6edf3otherTb4b4b4: Tffa657Any?Tb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72this Tff7b72=Tff7b72=Tff7b72= Te6edf3otherTb4b4b4) Tff7b72return Tff7b72true
Tff7b72if Tb4b4b4(Te6edf3other Tff7b72!is Te6edf3FakeBleWriteTb4b4b4) Tff7b72return Tff7b72false
Tff7b72return Te6edf3characteristic Tff7b72=Tff7b72= Te6edf3otherTb4b4b4.Te6edf3characteristic Tff7b72&Tff7b72& Te6edf3dataTb4b4b4.Te6edf3contentEqualsTb4b4b4(Te6edf3otherTb4b4b4.Te6edf3dataTb4b4b4) Tff7b72&Tff7b72& Te6edf3writeType Tff7b72=Tff7b72= Te6edf3otherTb4b4b4.Te6edf3writeType
Tb4b4b4}
Tff7b72override Tff7b72fun Td2a8ffhashCodeTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657Int Tff7b72= T79c0ff3T79c0ff1 Tff7b72* Tb4b4b4(T79c0ff3T79c0ff1 Tff7b72* Te6edf3characteristicTb4b4b4.Te6edf3hashCodeTb4b4b4(Tb4b4b4) Tff7b72+ Te6edf3dataTb4b4b4.Te6edf3contentHashCodeTb4b4b4(Tb4b4b4)Tb4b4b4) Tff7b72+ Te6edf3writeTypeTb4b4b4.Te6edf3hashCodeTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72class T56d364FakeBleService Tb4b4b4: Te6edf3BleService Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3availableCharacteristics Tff7b72= Te6edf3mutableSetOfTff7b72<Te6edf3UuidTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3notificationFlows Tff7b72= Te6edf3mutableMapOfTff7b72<Te6edf3UuidTb4b4b4, Te6edf3MutableSharedFlowTff7b72<Te6edf3ByteArrayTff7b72>Tff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3readQueues Tff7b72= Te6edf3mutableMapOfTff7b72<Te6edf3UuidTb4b4b4, Te6edf3MutableListTff7b72<Te6edf3ByteArrayTff7b72>Tff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3writes Tff7b72= Te6edf3mutableListOfTff7b72<Te6edf3FakeBleWriteTff7b72>Tb4b4b4(Tb4b4b4)
T8b949e/** Counts every write invocation, including attempts that fail before they can be recorded in [writes]. */
Tff7b72var Te6edf3writeAttemptsTb4b4b4: Tffa657Int Tff7b72= T79c0ff0
Te6edf3private Tff7b72set
T8b949e/** When non-null, [write] throws this exception on every call until explicitly cleared. */
Tff7b72var Te6edf3writeExceptionTb4b4b4: Te6edf3Exception? Tff7b72= Tff7b72null
T8b949e/**
* Optional suspend hook invoked after the attempt is counted and before [writeException] is evaluated, so it also
* runs for writes that then fail.
*/
Tff7b72var Te6edf3beforeWriteTb4b4b4: Tb4b4b4(Te6edf3suspend Tb4b4b4(Te6edf3BleCharacteristicTb4b4b4, Te6edf3ByteArrayTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4)Tff7b72? Tff7b72= Tff7b72null
T8b949e/**
* When non-null, [read] throws this exception instead of returning data. Reset to null before throwing (in the same
* call).
*/
Tff7b72var Te6edf3readExceptionTb4b4b4: Te6edf3Exception? Tff7b72= Tff7b72null
T8b949e/**
* When non-null, [observe] returns a flow that immediately throws. Reset to null when observe() is called (before
* flow collection).
*/
Tff7b72var Te6edf3observeExceptionTb4b4b4: Te6edf3Exception? Tff7b72= Tff7b72null
T8b949e/** Characteristic-specific observe failures that occur when the returned flow is collected. */
Tff7b72val Te6edf3observeExceptionsByCharacteristicTb4b4b4: Te6edf3MutableMapTff7b72<Te6edf3UuidTb4b4b4, Te6edf3ExceptionTff7b72> Tff7b72= Te6edf3mutableMapOfTb4b4b4(Tb4b4b4)
T8b949e/** Characteristic-specific observe failures that occur before [BleService.observe]'s onSubscription callback. */
Tff7b72val Te6edf3observeBeforeSubscriptionExceptionByCharacteristicTb4b4b4: Te6edf3MutableMapTff7b72<Te6edf3UuidTb4b4b4, Te6edf3ExceptionTff7b72> Tff7b72= Te6edf3mutableMapOfTb4b4b4(Tb4b4b4)
T8b949e/** Characteristics whose 2-arg [observe] never invokes onSubscription. Notifications can still be emitted. */
Tff7b72val Te6edf3observeNeverSubscribeCharacteristicsTb4b4b4: Te6edf3MutableSetTff7b72<Te6edf3UuidTff7b72> Tff7b72= Te6edf3mutableSetOfTb4b4b4(Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffhasCharacteristicTb4b4b4(Te6edf3characteristicTb4b4b4: Te6edf3BleCharacteristicTb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72=
Te6edf3availableCharacteristicsTb4b4b4.Te6edf3containsTb4b4b4(Te6edf3characteristicTb4b4b4.Te6edf3uuidTb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffobserveTb4b4b4(Te6edf3characteristicTb4b4b4: Te6edf3BleCharacteristicTb4b4b4)Tb4b4b4: Te6edf3FlowTff7b72<Te6edf3ByteArrayTff7b72> Tb4b4b4{
Tff7b72val Te6edf3failure Tff7b72=
Te6edf3observeExceptionsByCharacteristicTb4b4b4.Te6edf3removeTb4b4b4(Te6edf3characteristicTb4b4b4.Te6edf3uuidTb4b4b4)
Tff7b72?: Te6edf3observeExceptionTff7b72?.Te6edf3also Tb4b4b4{ Te6edf3observeException Tff7b72= Tff7b72null Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3failure Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3flow Tb4b4b4{ Tff7b72throw Te6edf3failure Tb4b4b4}
Tb4b4b4}
Tff7b72return Te6edf3notificationFlowsTb4b4b4.Te6edf3getOrPutTb4b4b4(Te6edf3characteristicTb4b4b4.Te6edf3uuidTb4b4b4) Tb4b4b4{ Te6edf3MutableSharedFlowTb4b4b4(Te6edf3extraBufferCapacity Tff7b72= T79c0ff1T79c0ff6Tb4b4b4) Tb4b4b4}
Tb4b4b4}
T8b949e/**
* Overrides the 2-arg observe to prevent false subscriptionReady when testing pre-readiness failures.
*
* The default BleService implementation calls `observe(characteristic).onStart { onSubscription() }`, which would
* invoke [onSubscription] BEFORE any pre-collection failure flow throws. This override throws BEFORE invoking
* [onSubscription], correctly simulating "observe failed before CCCD/subscription readiness."
*
* Pre-readiness failures are sourced (in priority order) from:
* - [observeBeforeSubscriptionExceptionByCharacteristic] (2-arg-specific, one-shot per uuid)
* - [observeExceptionsByCharacteristic] (shared with 1-arg observe, one-shot per uuid; defensively treated as a
* pre-subscription failure here so the bare onStart wrap cannot swallow it after [onSubscription] runs)
* - [observeException] (global, one-shot)
*
* For characteristics in [observeNeverSubscribeCharacteristics], [onSubscription] is never invoked but
* notifications are still exposed โ the returned flow is the bare SharedFlow with no [onStart] wrap, so
* [emitNotification] still reaches active collectors.
*/
Tff7b72override Tff7b72fun Td2a8ffobserveTb4b4b4(Te6edf3characteristicTb4b4b4: Te6edf3BleCharacteristicTb4b4b4, Te6edf3onSubscriptionTb4b4b4: Te6edf3suspend Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4)Tb4b4b4: Te6edf3FlowTff7b72<Te6edf3ByteArrayTff7b72> Tb4b4b4{
Tff7b72val Te6edf3failure Tff7b72=
Te6edf3observeBeforeSubscriptionExceptionByCharacteristicTb4b4b4.Te6edf3removeTb4b4b4(Te6edf3characteristicTb4b4b4.Te6edf3uuidTb4b4b4)
Tff7b72?: Te6edf3observeExceptionsByCharacteristicTb4b4b4.Te6edf3removeTb4b4b4(Te6edf3characteristicTb4b4b4.Te6edf3uuidTb4b4b4)
Tff7b72?: Te6edf3observeExceptionTff7b72?.Te6edf3also Tb4b4b4{ Te6edf3observeException Tff7b72= Tff7b72null Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3failure Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
T8b949e// onSubscription is NOT invoked โ simulates failure before CCCD/subscription readiness.
Tff7b72return Te6edf3flow Tb4b4b4{ Tff7b72throw Te6edf3failure Tb4b4b4}
Tb4b4b4}
Tff7b72val Te6edf3base Tff7b72= Te6edf3observeTb4b4b4(Te6edf3characteristicTb4b4b4)
Tff7b72return Tff7b72if Tb4b4b4(Te6edf3characteristicTb4b4b4.Te6edf3uuid Tff7b72in Te6edf3observeNeverSubscribeCharacteristicsTb4b4b4) Tb4b4b4{
Te6edf3base
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3baseTb4b4b4.Te6edf3onStart Tb4b4b4{ Te6edf3onSubscriptionTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffreadTb4b4b4(Te6edf3characteristicTb4b4b4: Te6edf3BleCharacteristicTb4b4b4)Tb4b4b4: Te6edf3ByteArray Tb4b4b4{
Te6edf3readExceptionTff7b72?.Te6edf3let Tb4b4b4{
Te6edf3readException Tff7b72= Tff7b72null
Tff7b72throw Tffa657it
Tb4b4b4}
Tff7b72return Te6edf3readQueuesTff7b72[Te6edf3characteristicTb4b4b4.Te6edf3uuidTff7b72]Tff7b72?.Te6edf3removeFirstOrNullTb4b4b4(Tb4b4b4) Tff7b72?: Te6edf3ByteArrayTb4b4b4(T79c0ff0Tb4b4b4)
Tb4b4b4}
Tff7b72override Tff7b72fun Td2a8ffpreferredWriteTypeTb4b4b4(Te6edf3characteristicTb4b4b4: Te6edf3BleCharacteristicTb4b4b4)Tb4b4b4: Te6edf3BleWriteType Tff7b72= Te6edf3BleWriteTypeTb4b4b4.Te6edf3WITH_RESPONSE
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffwriteTb4b4b4(Te6edf3characteristicTb4b4b4: Te6edf3BleCharacteristicTb4b4b4, Te6edf3dataTb4b4b4: Te6edf3ByteArrayTb4b4b4, Te6edf3writeTypeTb4b4b4: Te6edf3BleWriteTypeTb4b4b4) Tb4b4b4{
Te6edf3writeAttemptsTff7b72+Tff7b72+
Te6edf3beforeWriteTff7b72?.Te6edf3invokeTb4b4b4(Te6edf3characteristicTb4b4b4, Te6edf3dataTb4b4b4)
Te6edf3writeExceptionTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3ex Tff7b72-Tff7b72> Tff7b72throw Te6edf3ex Tb4b4b4}
Te6edf3availableCharacteristics Tff7b72+Tff7b72= Te6edf3characteristicTb4b4b4.Te6edf3uuid
Te6edf3writes Tff7b72+Tff7b72= Te6edf3FakeBleWriteTb4b4b4(Te6edf3characteristic Tff7b72= Te6edf3characteristicTb4b4b4, Te6edf3data Tff7b72= Te6edf3dataTb4b4b4.Te6edf3copyOfTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3writeType Tff7b72= Te6edf3writeTypeTb4b4b4)
Tb4b4b4}
Tff7b72fun Td2a8ffaddCharacteristicTb4b4b4(Te6edf3uuidTb4b4b4: Te6edf3UuidTb4b4b4) Tb4b4b4{
Te6edf3availableCharacteristics Tff7b72+Tff7b72= Te6edf3uuid
Tb4b4b4}
Tff7b72fun Td2a8ffemitNotificationTb4b4b4(Te6edf3uuidTb4b4b4: Te6edf3UuidTb4b4b4, Te6edf3dataTb4b4b4: Te6edf3ByteArrayTb4b4b4) Tb4b4b4{
Te6edf3availableCharacteristics Tff7b72+Tff7b72= Te6edf3uuid
Te6edf3notificationFlowsTb4b4b4.Te6edf3getOrPutTb4b4b4(Te6edf3uuidTb4b4b4) Tb4b4b4{ Te6edf3MutableSharedFlowTb4b4b4(Te6edf3extraBufferCapacity Tff7b72= T79c0ff1T79c0ff6Tb4b4b4) Tb4b4b4}Tb4b4b4.Te6edf3tryEmitTb4b4b4(Te6edf3dataTb4b4b4)
Tb4b4b4}
Tff7b72fun Td2a8ffenqueueReadTb4b4b4(Te6edf3uuidTb4b4b4: Te6edf3UuidTb4b4b4, Te6edf3dataTb4b4b4: Te6edf3ByteArrayTb4b4b4) Tb4b4b4{
Te6edf3availableCharacteristics Tff7b72+Tff7b72= Te6edf3uuid
Te6edf3readQueuesTb4b4b4.Te6edf3getOrPutTb4b4b4(Te6edf3uuidTb4b4b4) Tb4b4b4{ Te6edf3mutableListOfTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4.Te6edf3addTb4b4b4(Te6edf3dataTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72class T56d364FakeBleConnectionFactoryTb4b4b4(Tff7b72private Tff7b72val Te6edf3fakeConnectionTb4b4b4: Te6edf3FakeBleConnection Tff7b72= Te6edf3FakeBleConnectionTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4:
Te6edf3BleConnectionFactory Tb4b4b4{
Tff7b72override Tff7b72fun Td2a8ffcreateTb4b4b4(Te6edf3scopeTb4b4b4: Te6edf3CoroutineScopeTb4b4b4, Te6edf3tagTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3BleConnection Tff7b72= Te6edf3fakeConnection
Tb4b4b4}
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffEmptyFunctionBlockTa5d6ff"Tb4b4b4)
Tff7b72class T56d364FakeBluetoothRepository Tb4b4b4:
Te6edf3BaseFakeTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3BluetoothRepository Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3_state Tff7b72= Te6edf3mutableStateFlowTb4b4b4(Te6edf3BluetoothStateTb4b4b4(Te6edf3hasPermissions Tff7b72= Tff7b72trueTb4b4b4, Te6edf3enabled Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4)
Tff7b72override Tff7b72val Te6edf3stateTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3BluetoothStateTff7b72> Tff7b72= Te6edf3_stateTb4b4b4.Te6edf3asStateFlowTb4b4b4(Tb4b4b4)
T8b949e/**
* Controls what [bond] does. Defaults to [BondOutcome.Success] so existing tests that never touch this knob keep
* the historical "bonding always succeeds" behavior. Set it (or use the [failBondWith] /
* [failBondWithSecurityException] helpers) to drive the failure paths of consumers such as
* `AndroidScannerViewModel.requestBonding`.
*/
Tff7b72var Te6edf3bondOutcomeTb4b4b4: Te6edf3BondOutcome Tff7b72= Te6edf3BondOutcomeTb4b4b4.Te6edf3Success
T8b949e/** Every device passed to [bond], in call order โ lets tests assert that bonding was (or was not) attempted. */
Tff7b72val Te6edf3bondCalls Tff7b72= Te6edf3mutableListOfTff7b72<Te6edf3BleDeviceTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72init Tb4b4b4{
Te6edf3registerResetAction Tb4b4b4{
Te6edf3bondOutcome Tff7b72= Te6edf3BondOutcomeTb4b4b4.Te6edf3Success
Te6edf3bondCallsTb4b4b4.Te6edf3clearTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72override Tff7b72fun Td2a8ffrefreshStateTb4b4b4(Tb4b4b4) Tb4b4b4{Tb4b4b4}
Tff7b72override Tff7b72fun Td2a8ffisValidTb4b4b4(Te6edf3bleAddressTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Te6edf3bleAddressTb4b4b4.Te6edf3isNotBlankTb4b4b4(Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffisBondedTb4b4b4(Te6edf3addressTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Te6edf3_stateTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3bondedDevicesTb4b4b4.Te6edf3any Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3address Tff7b72=Tff7b72= Te6edf3address Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffbondTb4b4b4(Te6edf3deviceTb4b4b4: Te6edf3BleDeviceTb4b4b4) Tb4b4b4{
Te6edf3bondCalls Tff7b72+Tff7b72= Te6edf3device
Tff7b72val Te6edf3error Tff7b72=
Tff7b72when Tb4b4b4(Tff7b72val Te6edf3outcome Tff7b72= Te6edf3bondOutcomeTb4b4b4) Tb4b4b4{
Tff7b72is Te6edf3BondOutcomeTb4b4b4.Te6edf3Security Tff7b72-Tff7b72> Te6edf3outcomeTb4b4b4.Te6edf3error
Tff7b72is Te6edf3BondOutcomeTb4b4b4.Te6edf3Fail Tff7b72-Tff7b72> Te6edf3outcomeTb4b4b4.Te6edf3error
Tff7b72is Te6edf3BondOutcomeTb4b4b4.Te6edf3FailAfterBond Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3addBondedDeviceTb4b4b4(Te6edf3deviceTb4b4b4)
Te6edf3outcomeTb4b4b4.Te6edf3error
Tb4b4b4}
Te6edf3BondOutcomeTb4b4b4.Te6edf3Success Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3addBondedDeviceTb4b4b4(Te6edf3deviceTb4b4b4)
Tff7b72null
Tb4b4b4}
Tb4b4b4}
Te6edf3errorTff7b72?.Te6edf3let Tb4b4b4{ Tff7b72throw Tffa657it Tb4b4b4}
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8ffaddBondedDeviceTb4b4b4(Te6edf3deviceTb4b4b4: Te6edf3BleDeviceTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3currentState Tff7b72= Te6edf3_stateTb4b4b4.Te6edf3value
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3currentStateTb4b4b4.Te6edf3bondedDevicesTb4b4b4.Te6edf3containsTb4b4b4(Te6edf3deviceTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3_stateTb4b4b4.Te6edf3value Tff7b72= Te6edf3currentStateTb4b4b4.Te6edf3copyTb4b4b4(Te6edf3bondedDevices Tff7b72= Te6edf3currentStateTb4b4b4.Te6edf3bondedDevices Tff7b72+ Te6edf3deviceTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72fun Td2a8ffsetBluetoothEnabledTb4b4b4(Te6edf3enabledTb4b4b4: Tffa657BooleanTb4b4b4) Tb4b4b4{
Te6edf3_stateTb4b4b4.Te6edf3value Tff7b72= Te6edf3_stateTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3copyTb4b4b4(Te6edf3enabled Tff7b72= Te6edf3enabledTb4b4b4)
Tb4b4b4}
Tff7b72fun Td2a8ffsetHasPermissionsTb4b4b4(Te6edf3hasPermissionsTb4b4b4: Tffa657BooleanTb4b4b4) Tb4b4b4{
Te6edf3_stateTb4b4b4.Te6edf3value Tff7b72= Te6edf3_stateTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3copyTb4b4b4(Te6edf3hasPermissions Tff7b72= Te6edf3hasPermissionsTb4b4b4)
Tb4b4b4}
T8b949e/**
* The outcome [FakeBluetoothRepository.bond] produces. [Fail] and [Security] both simply throw their wrapped error;
* the distinct cases exist only to document caller intent (via [failBondWith] vs [failBondWithSecurityException])
* and leave a seam should the fake ever need to branch on permission failures.
*/
Tff7b72sealed Tff7b72interface T56d364BondOutcome Tb4b4b4{
T8b949e/** bond() completes normally and records the device as bonded (pre-existing default behavior). */
Tff7b72data Tff7b72object T56d364Success Tb4b4b4: Te6edf3BondOutcome
T8b949e/** bond() throws [error] โ models a generic/flaky bonding failure (timeout, dropped broadcast, etc.). */
Tff7b72data Tff7b72class T56d364FailTb4b4b4(Tff7b72val Te6edf3errorTb4b4b4: Te6edf3ThrowableTb4b4b4) Tb4b4b4: Te6edf3BondOutcome
T8b949e/** bond() records the device as bonded, then throws [error] โ models a lost terminal bond signal. */
Tff7b72data Tff7b72class T56d364FailAfterBondTb4b4b4(Tff7b72val Te6edf3errorTb4b4b4: Te6edf3ThrowableTb4b4b4) Tb4b4b4: Te6edf3BondOutcome
T8b949e/** bond() throws [error] โ models a missing-permission (BLUETOOTH_CONNECT) failure. */
Tff7b72data Tff7b72class T56d364SecurityTb4b4b4(Tff7b72val Te6edf3errorTb4b4b4: Te6edf3ThrowableTb4b4b4) Tb4b4b4: Te6edf3BondOutcome
Tb4b4b4}
Tb4b4b4}
T8b949e/** Make the next [FakeBluetoothRepository.bond] call throw a generic [error] (the flaky/interrupted-bonding path). */
Tff7b72fun Te6edf3FakeBluetoothRepositoryTb4b4b4.Td2a8fffailBondWithTb4b4b4(Te6edf3errorTb4b4b4: Te6edf3Throwable Tff7b72= Te6edf3ExceptionTb4b4b4(Ta5d6ff"Ta5d6ffbond failedTa5d6ff"Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3bondOutcome Tff7b72= Te6edf3FakeBluetoothRepositoryTb4b4b4.Te6edf3BondOutcomeTb4b4b4.Te6edf3FailTb4b4b4(Te6edf3errorTb4b4b4)
Tb4b4b4}
T8b949e/** Make the next [FakeBluetoothRepository.bond] call record the device as bonded and then throw [error]. */
Tff7b72fun Te6edf3FakeBluetoothRepositoryTb4b4b4.Td2a8fffailBondAfterRecordingTb4b4b4(Te6edf3errorTb4b4b4: Te6edf3Throwable Tff7b72= Te6edf3ExceptionTb4b4b4(Ta5d6ff"Ta5d6ffbond failed after pairingTa5d6ff"Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3bondOutcome Tff7b72= Te6edf3FakeBluetoothRepositoryTb4b4b4.Te6edf3BondOutcomeTb4b4b4.Te6edf3FailAfterBondTb4b4b4(Te6edf3errorTb4b4b4)
Tb4b4b4}
Served by rngit 1.5.0 - Generated in 0.1s